home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / DIConfig / flextree.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  27.5 KB  |  1,325 lines

  1. //-----------------------------------------------------------------------------
  2. // File: flextree.cpp
  3. //
  4. // Desc: Implements a tree class, similar to a Windows tree control,
  5. //       based on CFlexWnd.  It is used by the page to display the action
  6. //       list when the user wishes to assign an action to a control.
  7. //
  8. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  9. //-----------------------------------------------------------------------------
  10.  
  11. #include "common.hpp"
  12.  
  13.  
  14. CFlexTree::CFlexTree() :
  15.     m_pRoot(NULL), m_bDirty(FALSE), m_bNeedPaintBkgnd(FALSE),
  16.     m_rgbBkColor(RGB(255,255,255)),
  17.     m_pCurSel(NULL), m_pLastAdded(NULL), m_bOwnerDraw(FALSE),
  18.     m_bVertSB(FALSE), m_bHorzSB(FALSE),
  19.     m_nVertSBWidth(11), m_nHorzSBHeight(11)
  20. {
  21.     RECT z = {0,0,0,0};
  22.     m_defmargin = z;
  23.     m_clDefNormal.dwMask = CLMF_ALL;
  24.     m_clDefSelected.dwMask = CLMF_ALL;
  25.     m_ptScrollOrigin.x = m_ptScrollOrigin.y = 0;
  26.     
  27.     // allocate root
  28.     m_pRoot = new CFTItem;
  29.     if (m_pRoot != NULL)
  30.     {
  31.         m_pRoot->SetRoot(this);
  32.         assert(m_pRoot->IsRoot());
  33.     }
  34. }
  35.  
  36. CFlexTree::~CFlexTree()
  37. {
  38.     if (m_pRoot)
  39.         delete m_pRoot;
  40.     m_pRoot = NULL;
  41. }
  42.  
  43. CFTItem::CFTItem()
  44. {
  45.     Init();
  46. }
  47.  
  48. void CFTItem::Init()
  49. {
  50.     m_pUserData = NULL;
  51.     m_bExpanded = FALSE;
  52.     m_pTree = NULL;
  53.     m_pParent = NULL;
  54.     m_pPrev = NULL;
  55.     m_pNext = NULL;
  56.     m_pFirst = NULL;
  57.     m_pLast = NULL;
  58.     m_ptszCaption = NULL;
  59.     m_nIndent = m_nWidth = m_nHeight = m_nBranchHeight = 0;
  60.     m_nChildIndent = 11;
  61.     RECT z = {0,0,0,0};
  62.     m_margin = z;
  63.     memset(&m_UserGUID, 0, sizeof(GUID));
  64.     SetCaption(TEXT(""));
  65. }
  66.  
  67. CFTItem::~CFTItem()
  68. {
  69.     // detach from parent (unless root)
  70.     if (!IsRoot())
  71.         Detach();
  72.  
  73.     // remove all children
  74.     FreeChildren();
  75.  
  76.     // free stuff
  77.     SetCaption(NULL);
  78. }
  79.  
  80. void CFTItem::SetRoot(CFlexTree *pTree)
  81. {
  82.     if (pTree == NULL)
  83.     {
  84.         assert(0);
  85.         return;
  86.     }
  87.  
  88.     SetTree(pTree);
  89.  
  90.     m_nIndent = 0;
  91.     m_nHeight = 0;
  92.     m_nWidth = 0;
  93.     m_nChildIndent = 0;
  94.  
  95.     m_bExpanded = TRUE;
  96.     POINT origin = {0, 0};
  97.     m_origin = origin;
  98. }
  99.  
  100. BOOL CFTItem::IsRoot() const
  101. {
  102.     return m_pTree != NULL && m_pParent == NULL;
  103. }
  104.  
  105. CFTItem *CFlexTree::GetFirstItem() const
  106. {
  107.     if (m_pRoot == NULL)
  108.         return NULL;
  109.  
  110.     return m_pRoot->GetFirstChild();
  111. }
  112.  
  113. CFTItem *CFlexTree::GetLastItem() const
  114. {
  115.     if (m_pRoot == NULL)
  116.         return NULL;
  117.  
  118.     return m_pRoot->GetLastChild();
  119. }
  120.  
  121. BOOL CFTItem::IsOnTree() const
  122. {
  123.     return m_pTree != NULL;
  124. }
  125.  
  126. BOOL CFTItem::IsAttached() const
  127. {
  128.     if (IsRoot())
  129.     {
  130.         assert(0);
  131.         return TRUE;
  132.     }
  133.  
  134.     return m_pParent != NULL;
  135. }
  136.  
  137. BOOL CFTItem::IsAlone() const
  138. {
  139.     return
  140.         m_pTree == NULL &&
  141.         m_pParent == NULL &&
  142.         m_pPrev == NULL &&
  143.         m_pNext == NULL &&
  144.         m_pFirst == NULL &&
  145.         m_pLast == NULL;
  146. }
  147.  
  148. void CFTItem::FreeChildren()
  149. {
  150.     while (m_pFirst != NULL)
  151.     {
  152.         CFTItem *pChild = m_pFirst;
  153.         delete pChild;
  154.     }
  155. }
  156.  
  157. #define FORALLCHILDREN(pChild) \
  158.     for (CFTItem *pChild = m_pFirst; pChild != NULL; pChild = pChild->m_pNext)
  159.  
  160. void CFTItem::SetTree(CFlexTree *pTree)
  161. {
  162.     // don't do anything if we've already got this tree
  163.     if (m_pTree == pTree)
  164.         return;
  165.  
  166.     // if we are currently on a tree, tell it to lose any potential dangling pointers to us
  167.     if (m_pTree)
  168.         m_pTree->LosePointer(this);
  169.  
  170.     // actually set this tree
  171.     m_pTree = pTree;
  172.  
  173.     // set all children to this tree
  174.     FORALLCHILDREN(pChild)
  175.         pChild->SetTree(pTree);
  176. }
  177.  
  178. void CFTItem::Detach()
  179. {
  180.     // don't allow root detachment
  181.     if (IsRoot())
  182.     {
  183.         assert(0);
  184.         return;
  185.     }
  186.  
  187.     // if we're already detached, do nothing
  188.     if (!IsAttached())
  189.         return;
  190.  
  191.     // unlink from parent
  192.     if (m_pParent->m_pFirst == this)
  193.         m_pParent->m_pFirst = m_pNext;
  194.     if (m_pParent->m_pLast == this)
  195.         m_pParent->m_pLast = m_pPrev;
  196.     m_pParent = NULL;
  197.  
  198.     // unlink from siblings
  199.     if (m_pPrev)
  200.         m_pPrev->m_pNext = m_pNext;
  201.     if (m_pNext)
  202.         m_pNext->m_pPrev = m_pPrev;
  203.     m_pPrev = m_pNext = NULL;
  204.  
  205.     // save tree because we're about to lose it
  206.     CFlexTree *pTree = m_pTree;
  207.  
  208.     // tell ourself and all children that we are no longer on a tree
  209.     SetTree(NULL);
  210.  
  211.     // the tree needs to be recalced
  212.     if (pTree)
  213.         SetTreeDirty(pTree);
  214. }
  215.  
  216. BOOL CFTItem::Attach(CFTItem *to, ATTACHREL rel)
  217. {
  218.     // can't attach root to anything, can't attach to nothing, and can't attach if already attached
  219.     if (IsRoot() || to == NULL || IsAttached())
  220.     {
  221.         assert(0);
  222.         return FALSE;
  223.     }
  224.  
  225.     // first make sure we're not attaching to root in an impossible way
  226.     if (to->IsRoot())
  227.         switch (rel)
  228.         {
  229.             // only the following are valid for attaching to root
  230.             case ATTACH_FIRSTCHILD:
  231.             case ATTACH_LASTCHILD:
  232.                 break;
  233.  
  234.             // all others are invalid
  235.             default:
  236.                 assert(0);
  237.                 return FALSE;
  238.         }
  239.  
  240.     // now convert attaching as first/last sibling to equiv first/last child of parent
  241.     switch (rel)
  242.     {
  243.         case ATTACH_FIRSTSIBLING:
  244.             return Attach(to->m_pParent, ATTACH_FIRSTCHILD);
  245.  
  246.         case ATTACH_LASTSIBLING:
  247.             return Attach(to->m_pParent, ATTACH_LASTCHILD);
  248.     }
  249.  
  250.     // send to the more specific attach function
  251.     switch (rel)
  252.     {
  253.         case ATTACH_FIRSTCHILD:
  254.             return Attach(to, NULL, to->m_pFirst);
  255.         
  256.         case ATTACH_LASTCHILD:
  257.             return Attach(to, to->m_pLast, NULL);
  258.  
  259.         case ATTACH_BEFORE:
  260.             return Attach(to->m_pParent, to->m_pPrev, to);
  261.  
  262.         case ATTACH_AFTER:
  263.             return Attach(to->m_pParent, to, to->m_pNext);
  264.  
  265.         default:
  266.             assert(0);    // unhandled rel
  267.             return FALSE;
  268.     }
  269. }
  270.  
  271. BOOL CFTItem::Attach(CFTItem *pParent, CFTItem *pPrev, CFTItem *pNext)
  272. {
  273.     // can't attach root to anything, can't attach to no parent, and can't attach if already attached
  274.     if (IsRoot() || pParent == NULL || IsAttached())
  275.     {
  276.         assert(0);
  277.         return FALSE;
  278.     }
  279.  
  280.     // prev/next, if provided, must be children of parent
  281.     if ((pPrev && pPrev->m_pParent != pParent) ||
  282.         (pNext && pNext->m_pParent != pParent))
  283.     {
  284.         assert(0);
  285.         return FALSE;
  286.     }
  287.  
  288.     // pPrev and pNext must be consecutive
  289.     if ((pPrev && pPrev->m_pNext != pNext) ||
  290.         (pNext && pNext->m_pPrev != pPrev))
  291.     {
  292.         assert(0);
  293.         return FALSE;
  294.     }
  295.  
  296.     // insert
  297.     if (pPrev)
  298.         pPrev->m_pNext = this;
  299.     else
  300.         pParent->m_pFirst = this;
  301.  
  302.     if (pNext)
  303.         pNext->m_pPrev = this;
  304.     else
  305.         pParent->m_pLast = this;
  306.  
  307.     // attach
  308.     m_pParent = pParent;
  309.     m_pPrev = pPrev;
  310.     m_pNext = pNext;
  311.  
  312.     // set the tree
  313.     SetTree(pParent->m_pTree);
  314.  
  315.     // tree needs to be recalced
  316.     SetTreeDirty();
  317.  
  318.     return TRUE;
  319. }
  320.  
  321. void CFlexTree::SetDirty()
  322. {
  323.     m_bDirty = TRUE;
  324.     Invalidate();
  325. }
  326.  
  327. void CFTItem::SetTreeDirty(CFlexTree *pTree)
  328. {
  329.     if (pTree == NULL)
  330.         pTree = m_pTree;
  331.  
  332.     if (pTree)
  333.         pTree->SetDirty();
  334. }
  335.  
  336. void CFTItem::SetWidth(int i)
  337. {
  338.     if (m_nWidth == i)
  339.         return;
  340.     m_nWidth = i;
  341.     SetTreeDirty();
  342. }
  343.  
  344. void CFTItem::SetHeight(int i)
  345. {
  346.     if (m_nHeight == i)
  347.         return;
  348.     m_nHeight = i;
  349.     SetTreeDirty();
  350. }
  351.  
  352. void CFTItem::SetIndent(int i)
  353. {
  354.     if (m_nIndent == i)
  355.         return;
  356.     m_nIndent = i;
  357.     SetTreeDirty();
  358. }
  359.  
  360. void CFTItem::SetChildIndent(int i)
  361. {
  362.     if (m_nChildIndent == i)
  363.         return;
  364.     m_nChildIndent = i;
  365.     SetTreeDirty();
  366. }
  367.  
  368. void CFlexTree::OnPaint(HDC hDC)
  369. {
  370.     HDC hBDC = NULL, hODC = NULL;
  371.     CBitmap *pbm = NULL;
  372.  
  373.     m_bNeedPaintBkgnd = TRUE;
  374.  
  375.     if (!InRenderMode())
  376.     {
  377.         hODC = hDC;
  378.         pbm = CBitmap::Create(GetClientSize(), m_rgbBkColor, hDC);
  379.         if (pbm != NULL)
  380.         {
  381.             hBDC = pbm->BeginPaintInto();
  382.             if (hBDC != NULL)
  383.             {
  384.                 hDC = hBDC;
  385.                 m_bNeedPaintBkgnd = FALSE;
  386.             }
  387.         }
  388.     }
  389.  
  390.     InternalPaint(hDC);
  391.  
  392.     if (!InRenderMode())
  393.     {
  394.         if (pbm != NULL)
  395.         {
  396.             if (hBDC != NULL)
  397.             {
  398.                 pbm->EndPaintInto(hBDC);
  399.                 pbm->Draw(hODC);
  400.             }
  401.             delete pbm;
  402.         }
  403.     }
  404. }
  405.  
  406. void CFlexTree::InternalPaint(HDC hDC)
  407. {
  408.     // get client rect
  409.     RECT rect;
  410.     GetClientRect(&rect);
  411.  
  412.     // get view rect (ideal coordinates we're viewing)
  413.     RECT view = rect;
  414.     OffsetRect(&view, m_ptScrollOrigin.x, m_ptScrollOrigin.y);
  415.  
  416.     // paint background if necessary
  417.     if (m_bNeedPaintBkgnd)
  418.     {
  419.         HBRUSH hBrush = CreateSolidBrush(m_rgbBkColor);
  420.         if (hBrush != NULL)
  421.         {
  422.             HGDIOBJ hOldBrush = SelectObject(hDC, hBrush);
  423.             HGDIOBJ hOldPen = SelectObject(hDC, GetStockObject(NULL_PEN));
  424.             RECT t = rect;
  425.             InflateRect(&t, 1, 1);
  426.             Rectangle(hDC, t.left, t.top, t.right, t.bottom);
  427.             SelectObject(hDC, hOldPen);
  428.             SelectObject(hDC, hOldBrush);
  429.             DeleteObject((HGDIOBJ)hBrush);
  430.         }
  431.     }
  432.  
  433.     // recalculate if necessary
  434.     Calc();
  435.  
  436.     // start with the first visible item
  437.     CFTItem *pItem = GetFirstVisibleItem();
  438.  
  439.     // draw until we go out of view
  440.     for (; pItem != NULL; pItem = pItem->GetNextOut())
  441.     {
  442.         RECT irect;
  443.         pItem->GetItemRect(irect);
  444.         if (irect.top >= view.bottom)
  445.             break;
  446.  
  447.         OffsetRect(&irect, -m_ptScrollOrigin.x, -m_ptScrollOrigin.y);
  448.         
  449.         POINT oldorg;
  450.         OffsetViewportOrgEx(hDC, irect.left, irect.top, &oldorg);
  451.         
  452.         if (!pItem->FireOwnerDraw(hDC))
  453.             pItem->OnPaint(hDC);
  454.  
  455.         SetViewportOrgEx(hDC, oldorg.x, oldorg.y, NULL);
  456.     }
  457.  
  458.     // Fill in the small square at bottom right corner if we have both scroll bars.
  459.     if (m_bVertSB && m_bHorzSB)
  460.     {
  461.         HBRUSH hBrush = CreateSolidBrush(m_clDefNormal.rgbLineColor);
  462.         if (hBrush != NULL)
  463.         {
  464.             HGDIOBJ hOldBrush = SelectObject(hDC, hBrush);
  465.  
  466.             HGDIOBJ hPen = CreatePen(PS_SOLID, 0, m_clDefNormal.rgbLineColor);
  467.             if (hPen != NULL)
  468.             {
  469.                 HGDIOBJ    hOldPen = SelectObject(hDC, hPen);
  470.  
  471.                 RECT rc = rect;
  472.                 SIZE size;
  473.                 size = m_VertSB.GetClientSize();
  474.                 rc.left = rc.right - size.cx;
  475.                 size = m_HorzSB.GetClientSize();
  476.                 rc.top = rc.bottom - size.cy;
  477.                 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
  478.  
  479.                 SelectObject(hDC, hOldPen);
  480.                 DeleteObject((HGDIOBJ) hPen);
  481.             }
  482.  
  483.             SelectObject(hDC, hOldBrush);
  484.             DeleteObject((HGDIOBJ)hBrush);
  485.         }
  486.     }
  487. }
  488.  
  489. void CFlexTree::Calc()
  490. {
  491.     if (!m_bDirty)    
  492.         return;
  493.     m_bDirty = FALSE;
  494.  
  495.     CalcItems();
  496.  
  497.     BOOL bH = FALSE, bV = FALSE;
  498.     if (m_pRoot != NULL)
  499.     {
  500.         SIZE view = GetClientSize();
  501.         SIZE all = {m_nTotalWidth, m_pRoot->m_nBranchHeight};
  502.  
  503.         for (int i = 0; i < 2; i++)
  504.         {
  505.             // Added GetFirstVisibleItem() check since we don't want scroll bar if nothing is to be
  506.             // displayed.
  507.             if (!bV && all.cy > view.cy && GetFirstVisibleItem())
  508.             {
  509.                 bV = TRUE;
  510.                 view.cx -= m_nVertSBWidth;
  511.             }
  512.  
  513.             if (!bH && all.cx > view.cx)
  514.             {
  515.                 bH = TRUE;
  516.                 view.cy -= m_nHorzSBHeight;
  517.             }
  518.         }
  519.     
  520.         if (bH)
  521.         {
  522.             m_HorzSB.SetValues(0, all.cx, view.cx, m_ptScrollOrigin.x);
  523.             MoveWindow(m_HorzSB.m_hWnd, 0, view.cy, view.cx, m_nHorzSBHeight, TRUE);
  524.         }
  525.  
  526.         if (bV)
  527.         {
  528.             m_VertSB.SetValues(0, all.cy, view.cy, m_ptScrollOrigin.y);
  529.             MoveWindow(m_VertSB.m_hWnd, view.cx, 0, m_nVertSBWidth, view.cy, TRUE);
  530.         }
  531.     }
  532.  
  533.     if (bH && !m_bHorzSB || !bH && m_bHorzSB)
  534.     {
  535.         ShowWindow(m_HorzSB.m_hWnd, bH ? SW_SHOW : SW_HIDE);
  536.         if (!bH)
  537.         {
  538.             m_ptScrollOrigin.x = 0;
  539.             Invalidate();
  540.         }
  541.     }
  542.     m_bHorzSB = bH;
  543.  
  544.     if (bV && !m_bVertSB || !bV && m_bVertSB)
  545.     {
  546.         ShowWindow(m_VertSB.m_hWnd, bV ? SW_SHOW : SW_HIDE);
  547.         if (!bV)
  548.         {
  549.             m_ptScrollOrigin.y = 0;
  550.             Invalidate();
  551.         }
  552.     }
  553.     m_bVertSB = bV;
  554. }
  555.  
  556. void CFlexTree::CalcItems()
  557. {
  558.     // can't do anything without root
  559.     if (m_pRoot == NULL)
  560.         return;
  561.  
  562.     // calculate the entire tree in out/down order starting with first child of root...
  563.     POINT origin = {0, 0};
  564.     CFTItem *pItem = m_pRoot->m_pFirst;
  565.     m_nTotalWidth = 0;
  566.     while (pItem != NULL)
  567.     {
  568.         // let this item know its out
  569.  
  570.         // get parent origin
  571.         CFTItem *pParent = pItem->m_pParent;
  572.         assert(pParent != NULL);
  573.  
  574.         // calc origin...
  575.  
  576.         // if we're the first child
  577.         if (pItem->m_pPrev == NULL)
  578.         {
  579.             // base origin on the parent
  580.             if (pParent)
  581.             {
  582.                 origin.x = pParent->m_origin.x - pParent->m_nIndent + pParent->m_nChildIndent + pItem->m_nIndent;
  583.                 origin.y = pParent->m_origin.y + pParent->m_nHeight;
  584.             }
  585.         }
  586.         else // otherwise
  587.         {
  588.             // base origin on the previous sibling
  589.             CFTItem *pPrev = pItem->m_pPrev;
  590.             assert(pPrev != NULL);
  591.             if (pPrev)
  592.             {
  593.                 origin.x = pPrev->m_origin.x - pPrev->m_nIndent + pItem->m_nIndent;
  594.                 origin.y = pPrev->m_origin.y + pPrev->m_nBranchHeight;
  595.             }
  596.         }
  597.  
  598.         // set origin
  599.         pItem->m_origin = origin;
  600.  
  601.         // see which direction we'll be going next
  602.         CFTItem *pNext = pItem->GetNextOut();
  603.         enum {RIGHT, DOWN, BACK, NOWHERE, INVALID} dir = INVALID;
  604.         if (pNext == NULL)
  605.             dir = NOWHERE;
  606.         else if (pNext == pItem->m_pNext)
  607.             dir = DOWN;
  608.         else if (pNext == pItem->m_pFirst)
  609.             dir = RIGHT;
  610.         else
  611.             dir = BACK;
  612.  
  613.         // if we're going down, back, or nowhere, we can complete this item's branchheight
  614.         switch (dir)
  615.         {
  616.             case DOWN:
  617.             case BACK:
  618.             case NOWHERE:
  619.                 pItem->m_nBranchHeight = pItem->m_nHeight;
  620.                 break;
  621.         }
  622.  
  623.         // calc for skipped items when going back
  624.         switch (dir)
  625.         {
  626.             case BACK:
  627.             case NOWHERE:
  628.             {
  629.                 CFTItem *pStop = NULL;
  630.                 if (dir == BACK)
  631.                     pStop = pNext->m_pParent;
  632.                 CFTItem *pWalk = pItem->m_pParent;
  633.                 while (1)
  634.                 {
  635.                     if (pWalk == pStop)
  636.                         break;
  637.  
  638.                     pWalk->m_nBranchHeight = pItem->m_origin.y +
  639.                         pItem->m_nBranchHeight - pWalk->m_origin.y;
  640.  
  641.                     pWalk = pWalk->m_pParent;
  642.                 }
  643.                 break;
  644.             }    
  645.  
  646.             case INVALID:
  647.                 assert(0);
  648.                 break;
  649.         }
  650.  
  651.         RECT rect;
  652.         pItem->GetItemRect(rect);
  653.         if (rect.right > m_nTotalWidth)
  654.             m_nTotalWidth = rect.right;
  655.  
  656.         // now go to next item
  657.         pItem = pNext;
  658.     }
  659. }
  660.  
  661. CFTItem *CFTItem::GetNextOut() const
  662. {
  663.     return GetNext(TRUE);
  664. }
  665.  
  666. CFTItem *CFTItem::GetNext(BOOL bOutOnly) const
  667. {
  668.     // if we have a child and we're expanded (or we're not looking for out only), return the first child (going 'right')
  669.     if ((m_bExpanded || !bOutOnly) && m_pFirst != NULL)
  670.         return m_pFirst;
  671.  
  672.     // if we have a next sibling, return it (going 'down')
  673.     if (m_pNext != NULL)
  674.         return m_pNext;
  675.  
  676.     // climb up parents until we get to one with another next sibling
  677.     for (CFTItem *pItem = m_pParent; pItem != NULL; pItem = pItem->m_pParent)
  678.         if (pItem->m_pNext != NULL)
  679.             return pItem->m_pNext;
  680.  
  681.     // if we didn't find one, we're done
  682.     return NULL;
  683. }
  684.  
  685. void CFTItem::GetItemRect(RECT &rect) const
  686. {
  687.     rect.left = m_origin.x;
  688.     rect.top = m_origin.y;
  689.     rect.right = rect.left + m_nWidth;
  690.     rect.bottom = rect.top + m_nHeight;
  691. }
  692.  
  693. void CFTItem::GetBranchRect(RECT &rect) const
  694. {
  695.     rect.left = m_origin.x;
  696.     rect.top = m_origin.y;
  697.     rect.right = rect.left + m_nWidth;
  698.     rect.bottom = rect.top + m_nBranchHeight;
  699. }
  700.  
  701. void CFTItem::SetCaption(LPCTSTR tszCaption)
  702. {
  703.     if (m_ptszCaption != NULL)
  704.         free(m_ptszCaption);
  705.  
  706.     if (tszCaption == NULL)
  707.         m_ptszCaption = NULL;
  708.     else
  709.         m_ptszCaption = _tcsdup(tszCaption);
  710.  
  711.     RecalcText();
  712. }
  713.  
  714. LPCTSTR CFTItem::GetCaption() const
  715. {
  716.     return m_ptszCaption;
  717. }
  718.  
  719. void CFTItem::SetMargin(const RECT &rect)
  720. {
  721.     m_margin = rect;
  722.     RecalcText();
  723. }
  724.  
  725. void CFTItem::RecalcText()
  726. {
  727.     // calculate size from text dimensions and margin
  728.     SIZE size = {0, 0};
  729.     if (HasCaption())
  730.     {
  731.         RECT trect = {0, 0, 1, 1};
  732.         HDC hDC = CreateCompatibleDC(NULL);
  733.         if (hDC != NULL)
  734.         {
  735.             HGDIOBJ hOld = NULL;
  736.             HFONT hFont = IsSelected() ? m_clSelected.hFont : m_clNormal.hFont;
  737.             if (hFont)
  738.                 hOld = SelectObject(hDC, hFont);
  739.             DrawText(hDC, m_ptszCaption, -1, &trect, DT_CALCRECT | DT_NOPREFIX);
  740.             if (hFont)
  741.                 SelectObject(hDC, hOld);
  742.             DeleteDC(hDC);
  743.             SIZE tsize = {trect.right - trect.left, trect.bottom - trect.top};
  744.             size = tsize;
  745.         }
  746.     }
  747.     SetWidth(m_margin.left + m_margin.right + size.cx);
  748.     SetHeight(m_margin.top + m_margin.bottom + size.cy);
  749.  
  750.     // redraw
  751.     Invalidate();
  752. }
  753.  
  754. void CFTItem::Invalidate()
  755. {
  756.     if (m_pTree)
  757.         m_pTree->Invalidate();
  758. }
  759.  
  760. typedef CArray<LPDIACTIONW, LPDIACTIONW &> RGLPDIACW;
  761.  
  762. void CFTItem::OnPaint(HDC hDC)
  763. {
  764.        // If we don't have a tree pointer yet, then we have not been initialized.  Do not paint in this case.
  765.     if (!GetTree())
  766.         return;
  767.     CAPTIONLOOK &cl = (IsSelected() && !GetTree()->GetReadOnly()) ? m_clSelected : m_clNormal;  // Always use normal color if read-only since we gray out everything.
  768.     ::SetBkMode(hDC, cl.nBkMode);
  769.     
  770.     LPDIACTIONW lpac = NULL;
  771.     if (m_pUserData)
  772.         lpac = ((RGLPDIACW *)m_pUserData)->GetAt(0);  // Get the DIACTION this item holds.
  773.     if (GetTree()->GetReadOnly() || (lpac && (lpac->dwFlags & DIA_APPFIXED)))  // If read-only or the action has DIA_APPFIXED flag, use gray color for texts.
  774.         ::SetTextColor(hDC, RGB(GetRValue(cl.rgbTextColor) >> 1, GetGValue(cl.rgbTextColor) >> 1, GetBValue(cl.rgbTextColor) >> 1));
  775.     else
  776.         ::SetTextColor(hDC, cl.rgbTextColor);
  777.     ::SetBkColor(hDC, cl.rgbBkColor);
  778.     HGDIOBJ hOld = NULL;
  779.     if (cl.hFont)
  780.         hOld = SelectObject(hDC, cl.hFont);
  781.     RECT trect = {m_margin.left, m_margin.top, m_margin.left + 1, m_margin.top + 1};
  782.     DrawText(hDC, m_ptszCaption, -1, &trect, DT_NOPREFIX | DT_NOCLIP);
  783.     if (cl.hFont)
  784.         SelectObject(hDC, hOld);
  785. }
  786.  
  787. BOOL CFlexTree::Create(HWND hParent, const RECT &rect, BOOL bVisible, BOOL bOwnerDraw)
  788. {
  789.     m_bOwnerDraw = bOwnerDraw;
  790.  
  791.     if (CFlexWnd::Create(hParent, rect, bVisible) == NULL)
  792.         return FALSE;
  793.  
  794.     FLEXSCROLLBARCREATESTRUCT cs;
  795.     cs.dwSize = sizeof(FLEXSCROLLBARCREATESTRUCT);
  796.     cs.min = 0;
  797.     cs.max = 10;
  798.     cs.page = 3;
  799.     cs.pos = 5;
  800.     cs.hWndParent = m_hWnd;
  801.     cs.hWndNotify = NULL;
  802.     cs.rect.left = 0;
  803.     cs.rect.top = 0;
  804.     cs.rect.right = 5;
  805.     cs.rect.bottom = 5;
  806.     cs.bVisible = FALSE;
  807.     cs.dwFlags = FSBF_VERT;
  808.     m_VertSB.Create(&cs);
  809.     cs.dwFlags = FSBF_HORZ;
  810.     m_HorzSB.Create(&cs);
  811.  
  812.     return TRUE;
  813. }
  814.  
  815. void CFlexTree::SetDefCaptionLook(const CAPTIONLOOK &cl, BOOL bSel)
  816. {
  817.     CAPTIONLOOK &set = bSel ? m_clDefSelected : m_clDefNormal;
  818.  
  819.     if (cl.dwMask & CLMF_TEXTCOLOR)
  820.         set.rgbTextColor = cl.rgbTextColor;
  821.     if (cl.dwMask & CLMF_BKCOLOR)
  822.         set.rgbBkColor = cl.rgbBkColor;
  823.     if (cl.dwMask & CLMF_LINECOLOR)
  824.         set.rgbLineColor = cl.rgbLineColor;
  825.     if (cl.dwMask & CLMF_BKMODE)
  826.         set.nBkMode = cl.nBkMode;
  827.     if (cl.dwMask & CLMF_BKEXTENDS)
  828.         set.bBkExtends = cl.bBkExtends;
  829.     if (cl.dwMask & CLMF_FONT)
  830.         set.hFont = cl.hFont;
  831. }
  832.  
  833. void CFlexTree::GetDefCaptionLook(CAPTIONLOOK &cl, BOOL bSel) const
  834. {
  835.     const CAPTIONLOOK &from = bSel ? m_clDefSelected : m_clDefNormal;
  836.  
  837.     cl = from;
  838.     cl.dwMask = CLMF_ALL;
  839. }
  840.  
  841. void CFlexTree::SetBkColor(COLORREF rgb)
  842. {
  843.     m_rgbBkColor = rgb;
  844.     Invalidate();
  845. }
  846.  
  847. COLORREF CFlexTree::GetBkColor() const
  848. {
  849.     return m_rgbBkColor;
  850. }
  851.  
  852. void CFlexTree::SetCurSel(CFTItem *pItem)
  853. {
  854.     if (pItem == m_pCurSel)
  855.         return;
  856.  
  857.     CFTItem *pOld = m_pCurSel;
  858.     m_pCurSel = pItem;
  859.  
  860.     if (pOld)
  861.         pOld->SelChangedInternal();
  862.     if (m_pCurSel)
  863.         m_pCurSel->SelChangedInternal();
  864.  
  865.     FireSelChanged(m_pCurSel, pOld);
  866.  
  867.     Invalidate();
  868. }
  869.  
  870. CFTItem *CFlexTree::GetCurSel() const
  871. {
  872.     return m_pCurSel;
  873. }
  874.  
  875. CFTItem *CFlexTree::FindItem(const GUID &guid, void *pUserData) const
  876. {
  877.     // go until we get to the item with specified guid and userdata
  878.     for (CFTItem *pItem = GetFirstItem(); pItem != NULL; pItem = pItem->GetNext())
  879.         if (pItem->IsUserGUID(guid) && pItem->GetUserData() == pUserData)
  880.             return pItem;
  881.  
  882.     // unless there isn't one
  883.     return NULL;
  884. }
  885.  
  886. CFTItem *CFlexTree::FindItemEx(const GUID &guid, DWORD dwUser, void *pUser) const
  887. {
  888.     // go until we get to the item with specified guid and found item returns true
  889.     for (CFTItem *pItem = GetFirstItem(); pItem != NULL; pItem = pItem->GetNext())
  890.         if (pItem->IsUserGUID(guid) && pItem->FoundItem(dwUser, pUser))
  891.             return pItem;
  892.  
  893.     // unless there isn't one
  894.     return NULL;
  895. }
  896.  
  897. void CFTItem::SetCaptionLook(const CAPTIONLOOK &cl, BOOL bSel)
  898. {
  899.     CAPTIONLOOK &set = bSel ? m_clSelected : m_clNormal;
  900.  
  901.     if (cl.dwMask & CLMF_TEXTCOLOR)
  902.         set.rgbTextColor = cl.rgbTextColor;
  903.     if (cl.dwMask & CLMF_BKCOLOR)
  904.         set.rgbBkColor = cl.rgbBkColor;
  905.     if (cl.dwMask & CLMF_LINECOLOR)
  906.         set.rgbLineColor = cl.rgbLineColor;
  907.     if (cl.dwMask & CLMF_BKMODE)
  908.         set.nBkMode = cl.nBkMode;
  909.     if (cl.dwMask & CLMF_BKEXTENDS)
  910.         set.bBkExtends = cl.bBkExtends;
  911.     if (cl.dwMask & CLMF_FONT)
  912.         set.hFont = cl.hFont;
  913.  
  914.     if (IsSelected() == bSel)
  915.         RecalcText();
  916. }
  917.  
  918. void CFTItem::GetCaptionLook(CAPTIONLOOK &cl, BOOL bSel) const
  919. {
  920.     const CAPTIONLOOK &from = bSel ? m_clSelected : m_clNormal;
  921.  
  922.     cl = from;
  923.     cl.dwMask = CLMF_ALL;
  924. }
  925.  
  926. void CFTItem::GetMargin(RECT &rect) const
  927. {
  928.     rect = m_margin;
  929. }
  930.  
  931. void CFTItem::SelChangedInternal()
  932. {
  933.     if (m_clNormal.hFont != m_clSelected.hFont)
  934.         RecalcText();
  935. }
  936.  
  937. CFTItem *CFlexTree::DefAddItem(LPCTSTR tszCaption, CFTItem *to, ATTACHREL rel)
  938. {
  939.     if (m_pRoot == NULL)
  940.         return NULL;
  941.  
  942.     if (!to)
  943.         return DefAddItem(tszCaption, rel);
  944.  
  945.     if (!IsMine(to))
  946.     {
  947.         assert(0);        // can't add relative to item that doesn't belong to this tree
  948.         return NULL;
  949.     }
  950.  
  951.     CFTItem *p = new CFTItem;
  952.     if (!p)
  953.         return NULL;
  954.  
  955.     p->SetCaptionLook(m_clDefNormal);
  956.     p->SetCaptionLook(m_clDefSelected, TRUE);
  957.     p->SetChildIndent(m_nDefChildIndent);
  958.     p->SetMargin(m_defmargin);
  959.     p->SetCaption(tszCaption);
  960.  
  961.     p->Attach(to, rel);
  962.  
  963.     return m_pLastAdded = p;
  964. }
  965.  
  966. CFTItem *CFlexTree::DefAddItem(LPCTSTR tszCaption, ATTACHREL rel)
  967. {
  968.     if (m_pRoot == NULL)
  969.         return NULL;
  970.  
  971.     assert(this != NULL);
  972.     if (this == NULL)        // prevent infinite recursion possibility
  973.         return NULL;
  974.  
  975.     if (!m_pLastAdded)
  976.         return DefAddItem(tszCaption, m_pRoot, ATTACH_LASTCHILD);
  977.     else
  978.         return DefAddItem(tszCaption, m_pLastAdded, rel);
  979. }
  980.  
  981. void CFlexTree::SetDefMargin(const RECT &rect)
  982. {
  983.     m_defmargin = rect;
  984. }
  985.  
  986. void CFlexTree::GetDefMargin(RECT &rect) const
  987. {
  988.     rect = m_defmargin;
  989. }
  990.  
  991. BOOL CFlexTree::IsMine(CFTItem *pItem)
  992. {
  993.     if (pItem == NULL)
  994.         return FALSE;
  995.  
  996.     return pItem->m_pTree == this;
  997. }
  998.  
  999. BOOL CFTItem::IsSelected() const
  1000. {
  1001.     if (!m_pTree)
  1002.         return FALSE;
  1003.  
  1004.     return m_pTree->m_pCurSel == this;
  1005. }
  1006.  
  1007. CFTItem *CFlexTree::GetFirstVisibleItem() const
  1008. {
  1009.     // get view rect (ideal coordinates we're viewing)
  1010.     RECT view;
  1011.     GetClientRect(&view);
  1012.     OffsetRect(&view, m_ptScrollOrigin.x, m_ptScrollOrigin.y);
  1013.  
  1014.     // start at first child of root
  1015.     CFTItem *pItem = m_pRoot->GetFirstChild();
  1016.     if (pItem == NULL)
  1017.         return NULL;
  1018.  
  1019.     // find first item in view
  1020.     RECT branch, irect;
  1021.     while (1)
  1022.     {
  1023.         // find first branch in view
  1024.         while (1)
  1025.         {
  1026.             pItem->GetBranchRect(branch);
  1027.             if (branch.bottom > view.top)
  1028.                 break;
  1029.  
  1030.             pItem = pItem->GetNextSibling();
  1031.             if (pItem == NULL)
  1032.                 return NULL;
  1033.         }
  1034.  
  1035.         // now actually go through items
  1036.         pItem->GetItemRect(irect);
  1037.         if (irect.bottom > view.top)
  1038.             break;
  1039.  
  1040.         pItem = pItem->GetNextOut();
  1041.         if (pItem == NULL)
  1042.             return NULL;
  1043.     }
  1044.     
  1045.     // we got it, so return it
  1046.     return pItem;    
  1047. }
  1048.  
  1049. CFTItem *CFlexTree::GetItemFromPoint(POINT point) const
  1050. {
  1051.     if (m_hWnd == NULL)
  1052.         return NULL;
  1053.  
  1054.     RECT rect;
  1055.     GetClientRect(&rect);
  1056.     if (!PtInRect(&rect, point))
  1057.         return NULL;
  1058.  
  1059.     for (CFTItem *pItem = GetFirstVisibleItem(); pItem != NULL; pItem = pItem->GetNextOut())
  1060.     {
  1061.         RECT irect;
  1062.         pItem->GetItemRect(irect);
  1063.         OffsetRect(&irect, -m_ptScrollOrigin.x, -m_ptScrollOrigin.y);
  1064.  
  1065.         if (irect.top >= rect.bottom)
  1066.             return NULL;
  1067.  
  1068.         if (PtInRect(&irect, point))
  1069.             return pItem;
  1070.     }
  1071.  
  1072.     return NULL;
  1073. }
  1074.  
  1075. void CFlexTree::OnMouseOver(POINT point, WPARAM fwKeys)
  1076. {
  1077.     // Send mouse over notification to page to update info box.
  1078.     HWND hParent = ::GetParent(m_hWnd);
  1079.     if (hParent)
  1080.         SendMessage(hParent, WM_FLEXTREENOTIFY, FTN_MOUSEOVER, NULL);
  1081.  
  1082.     CFTItem *pItem = GetItemFromPoint(point);
  1083.     if (!pItem)
  1084.         return;
  1085.     POINT rel = {point.x - pItem->m_origin.x, point.y - pItem->m_origin.y};
  1086.     pItem->OnMouseOver(point, fwKeys);
  1087. }
  1088.  
  1089. void CFlexTree::OnClick(POINT point, WPARAM fwKeys, BOOL bLeft)
  1090. {
  1091.     // If the tree is read-only, ignore all clicks.
  1092.     if (GetReadOnly())
  1093.         return;
  1094.  
  1095.     CFTItem *pItem = GetItemFromPoint(point);
  1096.     if (!pItem)
  1097.     {
  1098.         FireClick(NULL, point, fwKeys, bLeft);
  1099.         return;
  1100.     }
  1101.     POINT rel = {point.x - pItem->m_origin.x, point.y - pItem->m_origin.y};
  1102.     pItem->OnClick(point, fwKeys, bLeft);
  1103. }
  1104.  
  1105. void CFTItem::OnClick(POINT point, WPARAM fwKeys, BOOL bLeft)
  1106. {
  1107.     FireClick(point, fwKeys, bLeft);
  1108. }
  1109.  
  1110. void CFlexTree::OnWheel(POINT point, WPARAM wParam)
  1111. {
  1112.     if (!m_bVertSB) return;
  1113.  
  1114.     int nPage = MulDiv(m_VertSB.GetPage(), 9, 10) >> 1;  // Half a page at a time
  1115.  
  1116.     if ((int)wParam >= 0)
  1117.         m_VertSB.AdjustPos(-nPage);
  1118.     else
  1119.         m_VertSB.AdjustPos(nPage);
  1120.  
  1121.     m_ptScrollOrigin.y = m_VertSB.GetPos();
  1122.     if (m_ptScrollOrigin.y < 0)
  1123.         m_ptScrollOrigin.y = 0;
  1124.     Invalidate();
  1125. }
  1126.  
  1127. void CFlexTree::FireClick(CFTItem *pItem, POINT point, WPARAM fwKeys, BOOL bLeft)
  1128. {
  1129.     FLEXTREENOTIFY n;
  1130.     n.pTree = this;
  1131.     n.pItem = pItem;
  1132.     n.pOldItem = NULL;
  1133.     n.hDC = NULL;
  1134.     n.point = point;
  1135.     n.fwKeys = fwKeys;
  1136.     n.bLeft = bLeft;
  1137.  
  1138.     HWND hParent = ::GetParent(m_hWnd);
  1139.     if (hParent)
  1140.         SendMessage(hParent, WM_FLEXTREENOTIFY, FTN_CLICK, (LRESULT)(LPVOID)&n);
  1141. }
  1142.  
  1143. BOOL CFlexTree::FireOwnerDraw(CFTItem *pItem, HDC hDC)
  1144. {
  1145.     if (!m_bOwnerDraw)
  1146.         return FALSE;
  1147.  
  1148.     FLEXTREENOTIFY n;
  1149.     n.pTree = this;
  1150.     n.pItem = pItem;
  1151.     n.pOldItem = NULL;
  1152.     n.hDC = hDC;
  1153.  
  1154.     HWND hParent = ::GetParent(m_hWnd);
  1155.     if (hParent)
  1156.         return (BOOL)SendMessage(hParent, WM_FLEXTREENOTIFY, FTN_OWNERDRAW, (LRESULT)(LPVOID)&n);
  1157.     else
  1158.         return FALSE;
  1159. }
  1160.  
  1161. void CFlexTree::FireSelChanged(CFTItem *pItem, CFTItem *pOld)
  1162. {
  1163.     assert(pItem == m_pCurSel);
  1164.  
  1165.     FLEXTREENOTIFY n;
  1166.     n.pTree = this;
  1167.     n.pItem = pItem;
  1168.     n.pOldItem = pOld;
  1169.     n.hDC = NULL;
  1170.  
  1171.     HWND hParent = ::GetParent(m_hWnd);
  1172.     if (hParent)
  1173.         SendMessage(hParent, WM_FLEXTREENOTIFY, FTN_SELCHANGED, (LRESULT)(LPVOID)&n);
  1174. }
  1175.  
  1176. void CFTItem::FireClick(POINT point, WPARAM fwKeys, BOOL bLeft)
  1177. {
  1178.     if (m_pTree)
  1179.         m_pTree->FireClick(this, point, fwKeys, bLeft);
  1180. }
  1181.  
  1182. BOOL CFTItem::FireOwnerDraw(HDC hDC)
  1183. {
  1184.     if (m_pTree)
  1185.         return m_pTree->FireOwnerDraw(this, hDC);
  1186.     else
  1187.         return FALSE;
  1188. }
  1189.  
  1190. void CFTItem::Expand(BOOL bAll)
  1191. {
  1192.     InternalExpand(TRUE, bAll);
  1193. }
  1194.  
  1195. void CFTItem::Collapse(BOOL bAll)
  1196. {
  1197.     InternalExpand(FALSE, bAll);
  1198. }
  1199.  
  1200. void CFTItem::InternalExpand(BOOL bExpand, BOOL bAll)
  1201. {
  1202.     if (!HasChildren())
  1203.         return;
  1204.  
  1205.     BOOL bE = m_bExpanded;
  1206.     if (!IsRoot())
  1207.         m_bExpanded = bExpand;
  1208.  
  1209.     if (bAll)
  1210.         FORALLCHILDREN(pChild)
  1211.             pChild->InternalExpand(bExpand, TRUE);
  1212.  
  1213.     if (bE != m_bExpanded)
  1214.         SetTreeDirty();
  1215. }
  1216.  
  1217. BOOL CFTItem::IsOut() const
  1218. {
  1219.     CFTItem *pParent = GetParent();
  1220.     for (; pParent != NULL; pParent = pParent->GetParent())
  1221.         if (!pParent->IsExpanded())
  1222.             return FALSE;
  1223.     return TRUE;
  1224. }
  1225.  
  1226. void CFlexTree::FreeAll()
  1227. {
  1228.     if (m_pRoot)
  1229.         m_pRoot->FreeChildren();
  1230. }
  1231.  
  1232. void CFTItem::EnsureVisible()
  1233. {
  1234.     // TBD
  1235. }
  1236.  
  1237. void CFlexTree::LosePointer(CFTItem *pItem)
  1238. {
  1239.     if (m_pCurSel == pItem)
  1240.         SetCurSel(NULL);
  1241.     if (m_pLastAdded == pItem)
  1242.         m_pLastAdded = NULL;
  1243. }
  1244.  
  1245. void CFlexTree::SetRootChildIndent(int i)
  1246. {
  1247.     if (!m_pRoot)
  1248.         return;
  1249.     m_pRoot->m_nChildIndent = i;
  1250.     SetDirty();
  1251. }
  1252.  
  1253. int CFlexTree::GetRootChildIndent() const
  1254. {
  1255.     if (!m_pRoot)
  1256.         return 0;
  1257.     return m_pRoot->m_nChildIndent;
  1258. }
  1259.  
  1260. void CFlexTree::SetDefChildIndent(int i)
  1261. {
  1262.     m_nDefChildIndent = i;
  1263. }
  1264.  
  1265. int CFlexTree::GetDefChildIndent() const
  1266. {
  1267.     return m_nDefChildIndent;
  1268. }
  1269.  
  1270. void CFTItem::PaintInto(HDC hDC)
  1271. {
  1272.     if (hDC != NULL)
  1273.         OnPaint(hDC);
  1274. }
  1275.  
  1276. LRESULT CFlexTree::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1277. {
  1278.     switch (msg)
  1279.     {
  1280.         case WM_FLEXVSCROLL:
  1281.         case WM_FLEXHSCROLL:
  1282.         {
  1283.             int code = (int)wParam;
  1284.             CFlexScrollBar *pSB = (CFlexScrollBar *)lParam;
  1285.             if (!pSB)
  1286.                 return 0;
  1287.  
  1288.             int nLine = 5;
  1289.             int nPage = MulDiv(pSB->GetPage(), 9, 10);
  1290.  
  1291.             switch (code)
  1292.             {
  1293.                 case SB_LINEUP: pSB->AdjustPos(-nLine); break;
  1294.                 case SB_LINEDOWN: pSB->AdjustPos(nLine); break;
  1295.                 case SB_PAGEUP: pSB->AdjustPos(-nPage); break;
  1296.                 case SB_PAGEDOWN: pSB->AdjustPos(nPage); break;
  1297.                 case SB_THUMBTRACK: pSB->SetPos(pSB->GetThumbPos()); break;
  1298.             }
  1299.  
  1300.             switch (msg)
  1301.             {
  1302.                 case WM_FLEXHSCROLL:
  1303.                     m_ptScrollOrigin.x = pSB->GetPos();
  1304.                     break;
  1305.  
  1306.                 case WM_FLEXVSCROLL:
  1307.                     m_ptScrollOrigin.y = pSB->GetPos();
  1308.                     break;
  1309.             }
  1310.  
  1311.             Invalidate();
  1312.             return 0;
  1313.         }
  1314.  
  1315.         default:
  1316.             return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  1317.     }
  1318. }
  1319.  
  1320. void CFlexTree::SetScrollBarColors(COLORREF bk, COLORREF fill, COLORREF line)
  1321. {
  1322.     m_VertSB.SetColors(bk, fill, line);
  1323.     m_HorzSB.SetColors(bk, fill, line);
  1324. }
  1325.